﻿using System;
using System.Reflection;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
 
namespace Obi{

	[System.AttributeUsage(System.AttributeTargets.Field)]
    public class SerializeProperty : PropertyAttribute
    {
        public string PropertyName { get; private set; }
 
        public SerializeProperty(string propertyName)
        {
            PropertyName = propertyName;
        }
    }
 
    #if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(SerializeProperty))]
    public class SerializePropertyAttributeDrawer : PropertyDrawer
    {
        private PropertyInfo propertyFieldInfo = null;
		private object target = null;
 
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
			if (target == null)
            	target = GetSource(property);
 
            // Find the property field using reflection, in order to get access to its getter/setter.
            if (propertyFieldInfo == null)
                propertyFieldInfo = target.GetType().GetProperty(((SerializeProperty)attribute).PropertyName,
                                                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
 
            if (propertyFieldInfo != null)
            {
  				// Retrieve the value using the property getter:
                object value = propertyFieldInfo.GetValue(target,null);
 
	            // Draw the property:
				EditorGUI.BeginProperty(position,label,property);
	            EditorGUI.BeginChangeCheck();
	            value = DrawProperty(position,property.propertyType,propertyFieldInfo.PropertyType,value,label);
	 
	            // If any changes were detected, call the property setter:
	            if (EditorGUI.EndChangeCheck() && propertyFieldInfo != null)
                {
                    foreach (var t in property.serializedObject.targetObjects)
                    {
                        // Record object state for undo:
                        Undo.RecordObject(t, "Inspector");

                        // Call property setter:
                        propertyFieldInfo.SetValue(t, value, null);
                        SetPropertyValue(property, propertyFieldInfo.PropertyType, value);

                        // Record prefab modification:
                        PrefabUtility.RecordPrefabInstancePropertyModifications(t);
                    }
                }
				EditorGUI.EndProperty();

			}else
            {
				EditorGUI.LabelField(position,"Error: could not retrieve property.");
			}
        }

		private object GetSource(SerializedProperty property)
        {
			object trgt = property.serializedObject.targetObject;
			string[] data = property.propertyPath.Split('.');
			
			if (data.Length == 1) 
				return trgt;
			else{
				for (int i = 0; i < data.Length-1;++i){
					trgt = trgt.GetType().GetField(data[i]).GetValue(trgt);
				}
			}
			
			return trgt;
		} 
 
        private object DrawProperty(Rect position, SerializedPropertyType propertyType, Type type, object value, GUIContent label)
        {
            switch (propertyType)
            {
                case SerializedPropertyType.Integer:
                    return EditorGUI.IntField(position,label,(int)value);
                case SerializedPropertyType.Boolean:
                    return EditorGUI.Toggle(position,label,(bool)value);
                case SerializedPropertyType.Float:
                    return EditorGUI.FloatField(position,label,(float)value);
                case SerializedPropertyType.String:
                    return EditorGUI.TextField(position,label,(string)value);
                case SerializedPropertyType.Color:
                    return EditorGUI.ColorField(position,label,(Color)value);
                case SerializedPropertyType.ObjectReference:
                    return EditorGUI.ObjectField(position,label,(UnityEngine.Object)value,type,true);
                case SerializedPropertyType.ExposedReference:
                    return EditorGUI.ObjectField(position,label,(UnityEngine.Object)value,type,true);
                case SerializedPropertyType.LayerMask:
                    return EditorGUI.LayerField(position,label,(int)value);
                case SerializedPropertyType.Enum:
                    return EditorGUI.EnumPopup(position,label,(Enum)value);
                case SerializedPropertyType.Vector2:
                    return EditorGUI.Vector2Field(position,label,(Vector2)value);
                case SerializedPropertyType.Vector3:
                    return EditorGUI.Vector3Field(position,label,(Vector3)value);
                case SerializedPropertyType.Vector4:
                    return EditorGUI.Vector4Field(position,label,(Vector4)value);
                case SerializedPropertyType.Rect:
                    return EditorGUI.RectField(position,label,(Rect)value);
                case SerializedPropertyType.AnimationCurve:
                    return EditorGUI.CurveField(position,label,(AnimationCurve)value);
                case SerializedPropertyType.Bounds:
                    return EditorGUI.BoundsField(position,label,(Bounds)value);
                default:
                    throw new NotImplementedException("Unimplemented propertyType "+propertyType+".");
            }
        }

        private void SetPropertyValue(SerializedProperty property, Type type, object value)
        {
            switch (property.propertyType)
            {
                case SerializedPropertyType.Integer: 
                    property.intValue = (int)value; break;
                case SerializedPropertyType.Boolean:
                    property.boolValue = (bool)value; break;
                case SerializedPropertyType.Float:
                    property.floatValue = (float)value; break;
                case SerializedPropertyType.String:
                    property.stringValue = (string)value; break;
                case SerializedPropertyType.Color:
                    property.colorValue = (Color)value; break;
                case SerializedPropertyType.ObjectReference:
                    property.objectReferenceValue = (UnityEngine.Object)value; break;
                case SerializedPropertyType.ExposedReference:
                    property.exposedReferenceValue = (UnityEngine.Object)value; break;
                case SerializedPropertyType.LayerMask:
                    property.intValue = (int)value; break;
                case SerializedPropertyType.Enum:
                    property.enumValueIndex = (int)value; break;
                case SerializedPropertyType.Vector2:
                    property.vector2Value = (Vector2)value; break;
                case SerializedPropertyType.Vector3:
                    property.vector3Value = (Vector3)value; break;
                case SerializedPropertyType.Vector4:
                    property.vector4Value = (Vector4)value; break;
                case SerializedPropertyType.Rect:
                    property.rectValue = (Rect)value; break;
                case SerializedPropertyType.AnimationCurve:
                    property.animationCurveValue = (AnimationCurve)value; break;
                case SerializedPropertyType.Bounds:
                    property.boundsValue = (Bounds)value; break;
                default:
                    throw new NotImplementedException("Unimplemented propertyType " + property.propertyType + ".");
            }
        }

    }
    #endif
}